package xyz.thoughtset.viewer.executor.blocks.executor;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.messages.*;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.tool.ToolExecutionResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;
import xyz.thoughtset.viewer.common.ai.model.entity.ModelParam;
import xyz.thoughtset.viewer.common.exc.exceptions.ExecException;
import xyz.thoughtset.viewer.modules.step.entity.AISupportBody;
import xyz.thoughtset.viewer.modules.step.entity.BlockTypeEnum;
import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo;

import java.util.*;


@Slf4j
@SuppressWarnings({"unchecked","SpringJavaInjectionPointsAutowiringInspection"})
public abstract class AIChatExecutor<T extends AISupportBody> extends AbstractAISupportBlockExecutor<T> {
    @Autowired
    protected ToolCallingManager toolCallingManager;
//    @Autowired
//    protected FunctionCallBlockToolCallbackProvider provider;


    protected abstract void loadToolCallbacks(BlockInfo block, T body, ChatClient.Builder builder);
    protected ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse){
        return toolCallingManager.executeToolCalls(prompt, chatResponse);
    }

//先选择函数，再加载参数执行函数
    @SneakyThrows
    @Override
    protected Object doQuery(BlockInfo block, T body, Map<String, Object> params, ExpressionParser parser, StandardEvaluationContext context) throws ExecException {

        HashMap filterMap = filterDataAsMapForPrompt(body, params, parser, context);
        ModelParam modelParam = loadModelParam(body);
        ChatClient.Builder builder = modelFactory.clientBuilder(modelParam);
        MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
                .maxMessages(modelParam.getMaxMemory())
                .build();

        ToolCallingChatOptions.Builder toolCallingBuilder = ToolCallingChatOptions.builder()
                .internalToolExecutionEnabled(false);
        String limitSystemPrompt =
                """
                    ---
                    # 所有未知信息都必须使用调用工具来获取。
                    # 部分上下文参数存储在本地，在调用参数时对于非required参数，可以填入建议值或空值。预期值与方法执行结果不一致时，以方法结果为准。
                    # 当调用多个工具时，必须按顺序依次调用，不允许跳过任何步骤。
                    # 工具调用后，必须等待工具返回结果，再根据结果决定是否继续调用下一个工具，不能跳过任何步骤。
                    # 当后续要求与当前冲突时，以当前要求为准! 
                    ---
                """+ System.lineSeparator();
        String systemText = modelParam.chatSystemPrompt();
        if (body.getJsonType()!=null && body.getJsonType().booleanValue()){

            systemText = """
            最终结果必须按照JSON数组格式返回，不要有其余任何内容,不要有任何拼写错误,不要有任何语法错误,当后续要求与当前冲突时，以当前要求为准!
            """
                    + (StringUtils.hasText(systemText) ? systemText : "");
        }
        String finalSystemText = limitSystemPrompt + System.lineSeparator() + systemText;
        builder.defaultSystem(fillAndRenderPromptToStr(finalSystemText, parser, context, filterMap));
        loadToolCallbacks(block,body,builder);
        ChatClient client = builder
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
                .build();
        ChatOptions chatOptions = toolCallingBuilder.build();
        UserMessage message = UserMessage.builder()
                .text(fillAndRenderPromptToStr(body.getUserMsg(), parser, context, filterMap))
                .build();
//                new UserMessage(body.getUserMsg(),params);
        String chatId = block.getId()+ UUID.randomUUID();
        saveMegToChatMemory(chatId, Collections.singletonList(message), chatMemory);
        Prompt chatPrompt = new Prompt(chatMemory.get(chatId),chatOptions);
        ChatResponse chatResponse = client
                .prompt(chatPrompt)
                .toolContext(filterMap).call().chatResponse();
        List<Message> messageList = new ArrayList<>();
        while (chatResponse.hasToolCalls()) {
            // 获取AI助手的响应消息
            AssistantMessage assistantMessage = chatResponse.getResult().getOutput();
            log.debug("***" + assistantMessage.getText());

            // 获取AI要求调用的工具列表
            List<AssistantMessage.ToolCall> toolCalls = assistantMessage.getToolCalls();
            toolCalls.forEach(toolCall -> {
                log.debug("*** 准备调用工具{}：{}，参数：({})", toolCall.type(), toolCall.name(), toolCall.arguments());
            });

            // 执行工具调用
            ToolExecutionResult toolExecutionResult = null;
            try {
                toolExecutionResult = executeToolCalls(chatPrompt, chatResponse);
            } catch (Exception e) {
                log.error("调用工具失败", e);
                messageList.add(new AssistantMessage("调用工具失败:" + e.getMessage()));
                break;
            }

            List<Message> toolResultMessages = toolExecutionResult.conversationHistory();
            log.debug("*** 执行结果" + toolResultMessages);

            Message lastMessage = toolResultMessages.get(toolResultMessages.size() - 1);
            if (lastMessage.getMessageType() == MessageType.TOOL) {
                ToolResponseMessage toolMessage = (ToolResponseMessage) lastMessage;
                toolMessage.getMetadata().put("toolArguments", toolCalls);
                messageList.add(toolMessage);

                // 记录每个工具调用的详细信息
                for (ToolResponseMessage.ToolResponse resp : toolMessage.getResponses()) {
                    log.info("*** {}#{}#{}",resp.id(),resp.name(),resp.responseData());
                }
            }
            saveMegToChatMemory(chatId, toolResultMessages, chatMemory);
            chatPrompt = new Prompt(chatMemory.get(chatId), chatOptions);
            chatResponse = client
                    .prompt(chatPrompt)
                    .call()
                    .chatResponse();
        }

        String val = chatResponse.getResult().getOutput().getText();
        log.debug("===结果:{}", val);
        Object result;

        try {
            result = objectMapper.readValue(val, DATA_TYPE);
        }catch (Exception e){
            if (!e.getClass().equals(com.fasterxml.jackson.core.JsonParseException.class)){
                log.error("错误结果:{}", val);
                e.printStackTrace();
            }
            result = val;
        }

        return result;
    }




}
